/*   Copyright 2004 BEA Systems, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package wx.xml.bea.xml.stream.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * Maintains a table for namespace scope
 * <p>
 * <p>
 * values = map from strings to stacks
 * [a]->[value1,0],[value2,0]
 * <p>
 * table = a stack of bindings
 */
public class SymbolTable {
    private int   depth;
    private Stack table;
    private Map   values;

    public SymbolTable() {
        depth = 0;
        table = new Stack();
        values = new HashMap();
    }

    public static void main(String args[])
        throws Exception {
        SymbolTable st = new SymbolTable();
        st.openScope();
        st.put("x", "foo");
        st.put("y", "bar");
        System.out.println("1 x:" + st.get("x"));
        System.out.println("1 y:" + st.get("y"));
        st.openScope();
        st.put("x", "bar");
        st.put("y", "foo");
        st.openScope();
        st.put("x", "barbie");
        st.openScope();
        st.closeScope();

        System.out.println("3 x:" + st.get("x"));
        st.closeScope();
        System.out.println("2 x:" + st.get("x"));
        System.out.println("2 y:" + st.get("y"));
        System.out.print(st);
        st.closeScope();
        System.out.println("1 x:" + st.get("x"));
        System.out.println("1 y:" + st.get("y"));
        st.closeScope();
        System.out.print(st);
    }

    public void clear() {
        depth = 0;
        table.clear();
        values.clear();
    }

    //
    // Gets the current depth
    //
    public int getDepth() {
        return depth;
    }

    public boolean withinElement() {
        return depth > 0;
    }

    //
    // Adds a name/value pair
    //
    public void put(String name, String value) {
        table.push(new Symbol(name, value, depth));
        if (!values.containsKey(name)) {
            Stack valueStack = new Stack();
            valueStack.push(value);
            values.put(name, valueStack);
        } else {
            Stack valueStack = (Stack) values.get(name);
            valueStack.push(value);
        }
    }

    //
    // Gets the value for a variable
    //
    public String get(String name) {
        Stack valueStack = (Stack) values.get(name);
        if (valueStack == null || valueStack.isEmpty())
            return null;
        return (String) valueStack.peek();
    }

    public Set getAll(String name) {
        HashSet  result = new HashSet();
        Iterator i      = table.iterator();
        while (i.hasNext()) {
            Symbol s = (Symbol) i.next();
            if (name.equals(s.getName()))
                result.add(s.getValue());
        }
        return result;
    }

    //
    // remove the highest level scope from the table
    //

    //
    // add a new highest level scope to the table
    //
    public void openScope() {
        depth++;
    }

    public void closeScope() {
        // Get the top binding
        Symbol symbol      = (Symbol) table.peek();
        int    symbolDepth = symbol.depth;

        // check if it needs to be popped of the table
        while (symbolDepth == depth && !table.isEmpty()) {
            symbol = (Symbol) table.pop();

            // pop its value as well
            Stack valueStack = (Stack) values.get(symbol.name);
            valueStack.pop();

            // check the next binding
            if (!table.isEmpty()) {
                symbol = (Symbol) table.peek();
                symbolDepth = symbol.depth;
            } else
                break;
        }
        depth--;
    }

    public String toString() {
        Iterator i      = table.iterator();
        String   retVal = "";
        while (i.hasNext()) {
            Symbol symbol = (Symbol) i.next();
            retVal = retVal + symbol + "\n";
        }
        return retVal;
    }
}
